package admin

import (
	"encoding/json"
	"exercise/application/yiran/app"
	"exercise/application/yiran/app/libs"
	"exercise/application/yiran/app/model"
	"exercise/application/yiran/app/request/adminRequest"
	"exercise/common/constant"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"path/filepath"
	"strconv"
	"sync"
	"time"
)

type userService struct {
}

var wg sync.WaitGroup

func (userService) ListJoin(ctx *gin.Context) (list []map[string]interface{}, err error) {

	list = make([]map[string]interface{}, 0)

	//err = model.NewDb("").Connect().Table("fd_admin_user as u").
	//	Select("ar.id as r_id,ar.role_name,u.id,u.nickname").
	//	Joins("LEFT JOIN fd_admin_role as ar ON u.role_id = ar.id ").
	//	Where("u.id = ?", 679).
	//	Scan(&list).Error

	tableAndJoin := model.TableAndJoin{}
	tableAndJoin.TableName = "fd_admin_user as u"
	tableAndJoin.Joins = []string{"LEFT JOIN fd_admin_role as ar ON u.role_id = ar.id"}
	tableAndJoin.Field = "ar.id as r_id,ar.role_name,u.id,u.nickname"

	count, err := model.NewDb("").FindJoinAllAndCount(tableAndJoin, &list, "u.id = ?", []interface{}{679}, model.OrderOrGroup{}, model.Paging{})

	fmt.Printf("count := %v \n", count)

	return list, err
}

// Detail 详情
func (u userService) Detail(ctx *gin.Context, request *adminRequest.DetailsRequest) (interface{}, error) {
	adminUser := new(model.AdminUser)
	err := model.NewDb("").FindOne(adminUser, "id = ?", []interface{}{request.Id}, "", nil)

	return adminUser, err
}

// Add 新增用户
func (userService) Add(ctx *gin.Context, request *adminRequest.AddUserRequest) (interface{}, error) {
	adminUser := new(model.AdminUser)
	schoolId, _ := ctx.Get("schoolId")
	schoolName, _ := ctx.Get("schoolName")
	adminUser.SchoolId = schoolId.(int64)
	adminUser.Username = request.Username
	adminUser.Nickname = request.Nickname
	adminUser.Mobile = request.Mobile
	adminUser.RoleId = request.RoleId
	adminUser.SchoolName = schoolName.(string)
	password, _ := libs.NewEncrypt().PasswordHash(request.Password + constant.SecretKey)
	adminUser.Password = password

	err := model.NewDb("").SaveRecord(adminUser)
	return adminUser.Id, err
}

// Edit 编辑
func (userService) Edit(ctx *gin.Context, request *adminRequest.EditUserRequest) (interface{}, error) {

	adminUser := new(model.AdminUser)

	id := request.Id
	schoolName, _ := ctx.Get("schoolName")

	//第一个种:先查询后修改
	/*err := model.NewDb("").FindOne(adminUser, "id = ?", []interface{}{id}, "*", nil)
	adminUser.Username = request.Username
	adminUser.Nickname = request.Nickname
	adminUser.Mobile = request.Mobile
	adminUser.RoleId = request.RoleId
	adminUser.SchoolName = schoolName.(string)
	password, _ := libs.PasswordHash(request.Password + constant.SecretKey)
	adminUser.Password = password
	err = model.NewDb("").SaveRecord(adminUser)*/

	//第二种：通过map修改指定字段
	whereSql := "id = ?"
	where := make([]interface{}, 0)
	where = append(where, id)

	password, _ := libs.NewEncrypt().PasswordHash(request.Password + constant.SecretKey)
	u := make(map[string]interface{}, 0)
	u["username"] = request.Username
	u["nickname"] = request.Nickname
	u["mobile"] = request.Mobile
	u["role_id"] = request.RoleId
	u["school_name"] = schoolName.(string)
	u["password"] = password

	err := model.NewDb("").UpdateRecord(adminUser, whereSql, where, u)

	return adminUser, err
}

// AddGo  新增
func (u userService) AddGo(ctx *gin.Context, request *adminRequest.AddUserRequest) (interface{}, error) {

	filename := filepath.Join(app.YiRanPublicDir, "1.json")
	jsonData, err := ioutil.ReadFile(filename)

	if err != nil {
		return nil, err
	}

	list := make([]map[string]interface{}, 0)
	err = json.Unmarshal(jsonData, &list)
	if err != nil {
		return nil, err
	}
	successChan := make(chan string, len(list))
	errChan := make(chan error, len(list))

	start := time.Now()
	batch := 1000
	for i := 0; i < len(list); i += batch {
		end := i + batch
		if len(list) <= batch {
			end = len(list)
		}
		wg.Add(1)
		go func(data []map[string]interface{}, i int) {
			defer wg.Done()
			userInfo := new(model.UserInfo)
			err = model.NewDb("").BatchInsertRecord(userInfo, data[:1])
			if err != nil { //异常了抛出
				errChan <- err
			} else {
				successChan <- "成功了" + strconv.Itoa(i)
			}
		}(list[i:end], i)
	}

	go func() {
		wg.Wait()
		close(errChan)
	}()

	wg.Add(1)
	go u.goErr(errChan)

	end := time.Now()
	diff := end.Sub(start)
	return int(diff.Microseconds()), nil
}

func (u userService) goErr(errChan chan error) []string {
	defer wg.Done()
	var errors []string
	for {
		select {
		case v, ok := <-errChan:
			if !ok {
				fmt.Println("errChan通道里的东西读完了")
				return errors
			}
			fmt.Printf("goErr 异常 %v \n", v.Error())
			errors = append(errors, v.Error())
		}
	}
	return errors
}

// AddSync  新增
func (u userService) AddSync(ctx *gin.Context, request *adminRequest.AddUserRequest) (interface{}, error) {

	filename := filepath.Join(app.YiRanPublicDir, "1.json")
	jsonData, err := ioutil.ReadFile(filename)

	if err != nil {
		return nil, err
	}

	list := make([]map[string]interface{}, 0)
	err = json.Unmarshal(jsonData, &list)
	if err != nil {
		return nil, err
	}
	successChan := make(chan string, len(list))
	errChan := make(chan error, len(list))

	start := time.Now()
	batch := 1000
	for i := 0; i < len(list); i += batch {
		end := i + batch
		if len(list) <= batch {
			end = len(list)
		}
		wg.Add(1)
		go func(data []map[string]interface{}, i int) {
			defer wg.Done()
			userInfo := new(model.UserInfo)
			err = model.NewDb("").BatchInsertRecord(userInfo, data[:1])
			if err != nil { //异常了抛出
				errChan <- err
			} else {
				successChan <- "成功了" + strconv.Itoa(i)
			}
		}(list[i:end], i)
	}

	/**
	为什么放在这里不阻塞?
		1.它会在所有协程完成任务之前等待
		2.开始读取successChan和errChan通道时,所有的goroutine都已经完成了任务,并且已经向这两个通道发送了数据
	*/
	go func() {
		wg.Wait()
		close(errChan)
		close(successChan)
	}()

	for s := range successChan {
		fmt.Printf("通道的中成功：%v \n", s)
	}

	for e := range errChan {
		fmt.Printf("通道中的错误：%v \n", e.Error())
	}

	/*
		放在这里就阻塞?
		1.所有的goroutine都被启动，并开始执行插入操作
		2.当这个协程被启动,由于这个函数实在一个新的协程中运行,主协程不等待它完成
		3.主goroutine立即开始读取successChan和errChan通道，但是此时可能还没有任何goroutine完成任务并将数据发送到这两个通道中。
		4.由于successChan和errChan通道没有数据，主goroutine中读取的地方会一直阻塞在for循环中，等待数据的到来
		5.当所有的goroutine完成任务并调用wg.Done()时，wg.Wait()才会结束，然后关闭successChan和errChan通道。但是，由于主goroutine已经在读取通道的for循环中阻塞，所以它无法继续执行并退出循环。

		go func() {
			wg.Wait()
			close(errChan)
			close(successChan)
		}()
	*/

	/*go func() {
		for s := range successChan {
			fmt.Printf("通道的中成功：%v \n", s)
		}

		for e := range errChan {
			fmt.Printf("通道中的错误：%v \n", e.Error())
		}

	}()*/

	end := time.Now()
	diff := end.Sub(start)
	return int(diff.Microseconds()), nil
}

func (u userService) writeLog(request *adminRequest.AddUserRequest) error {
	list := make([]map[string]string, 0)
	for i := 0; i < 8000; i++ {
		item := make(map[string]string, 0)
		item["username"] = request.Username + strconv.Itoa(i)
		item["nickname"] = request.Nickname + strconv.Itoa(i)
		list = append(list, item)
	}
	filename := filepath.Join(app.YiRanPublicDir, "1.json")
	jsonList, _ := json.Marshal(list)

	err := ioutil.WriteFile(filename, jsonList, 0755)
	return err
}

func NewUserService() *userService {
	return &userService{}
}
